package work.linruchang.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import wiki.xsx.core.pdf.component.XEasyPdfComponent;
import wiki.xsx.core.pdf.component.image.XEasyPdfImage;
import wiki.xsx.core.pdf.component.image.XEasyPdfImageType;
import wiki.xsx.core.pdf.doc.XEasyPdfDocument;
import wiki.xsx.core.pdf.doc.XEasyPdfPage;
import wiki.xsx.core.pdf.doc.XEasyPdfPageRectangle;
import wiki.xsx.core.pdf.handler.XEasyPdfHandler;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 增强的x-easypdf处理封装工具
 * <p>
 * 官网：https://www.x-easypdf.cn/#/
 *
 * @author LinRuChang
 * @version 1.0
 * @date 2022/09/03
 * @since 1.8
 **/
@Slf4j
public class EnhanceEasyPdfUtil {

    /**
     * 获取图片类型
     * @param imageFile
     * @return
     */
    public static XEasyPdfImageType getImageType(File imageFile) {
        return getImageType(FileUtil.extName(imageFile));
    }

    /**
     * 获取图片类型
     * @param fileType
     * @return
     */
    public static XEasyPdfImageType getImageType(String fileType) {
        return EnumUtil.getBy(XEasyPdfImageType.class, xEasyPdfImageType -> {
            String currentFileType = StrUtil.equalsAnyIgnoreCase(fileType,ImgUtil.IMAGE_TYPE_JPG,ImgUtil.IMAGE_TYPE_JPEG) ? ImgUtil.IMAGE_TYPE_JPEG:fileType;
            return StrUtil.equalsIgnoreCase(xEasyPdfImageType.name(), currentFileType);
        });
    }

    /**
     * 本地文件转成Itex的图片对象  【转换失败不抛出异常直接返回null】
     *
     * @param imageFile
     * @return
     */
    public static XEasyPdfComponent toEasyPdfImage(File imageFile) {
        return toEasyPdfImage(imageFile, 1);
    }


    /**
     * 本地文件转成Itex的图片对象  【转换失败不抛出异常直接返回null】
     *
     * @param imageFile    图片文件
     * @param imageQuality 压缩质量 0~1之前
     * @return
     */
    public static XEasyPdfComponent toEasyPdfImage(File imageFile, Number imageQuality) {
        imageQuality = ObjectUtil.defaultIfNull(imageQuality, 1);
        Assert.checkBetween(imageQuality, 0, 1);
        if (FileUtil.isFile(imageFile)) {
            try {
                XEasyPdfImage imageData = null;
                if (Convert.toInt(imageQuality) != 1) {
                    byte[] compressByteArray = EnhanceImgUtil.compress(imageFile, imageQuality);
                    imageData = XEasyPdfHandler.Image.build(new ByteArrayInputStream(compressByteArray), XEasyPdfImageType.JPEG);
                } else {
                    imageData = XEasyPdfHandler.Image.build(imageFile);
                }
                return imageData;
            } catch (Exception e) {
                log.error("错误：转换Easy-Pdf图片对象：{} \n {}", FileUtil.getAbsolutePath(imageFile), ExceptionUtil.stacktraceToOneLineString(e));
                return null;
            }
        }
        return null;
    }


    /**
     * 自定义新的宽高PDF页面，如果width、height有任一一个入参含null，都会使用默认A4页面
     *
     * @param width  宽
     * @param height 高
     * @return
     */
    public static XEasyPdfPage customNewPage(Number width, Number height) {
        return XEasyPdfHandler.Page.build(XEasyPdfPageRectangle.create(Convert.toFloat(width), Convert.toFloat(height)));
    }

    /**
     * 新的pdf页面根据图片的宽高来定义
     *
     * @param image 图片
     * @return
     */
    public static XEasyPdfPage customNewPageByImage(XEasyPdfImage image) {
        Object xEasyPdfImageParam = ReflectUtil.getFieldValue(image, "param");
        BufferedImage bufferedImage = (BufferedImage) ReflectUtil.getFieldValue(xEasyPdfImageParam, "image");
        return customNewPage(bufferedImage.getWidth(),bufferedImage.getHeight());
    }

    /**
     * 新的pdf页面根据图片的宽高来定义
     *
     * @param image 图片
     * @return
     */
    public static XEasyPdfPage customNewPageByImage(File image) {
        if(FileUtil.isFile(image)) {
            BufferedImage bufferedImage = ImgUtil.read(image);
            return customNewPage(bufferedImage.getWidth(), bufferedImage.getHeight());
        }
        return null;
    }

    /**
     * 图片转PDF（每张图就是每页的PDF（每张根据图片的宽高进行调整））
     * 文档：https://www.x-easypdf.cn/#/md/pdfbox/%E5%8A%A9%E6%89%8B%E8%AF%B4%E6%98%8E?id=%e5%9b%be%e7%89%87%e6%9e%84%e5%bb%ba
     *
     * @param imageFiles        一系列图片文件
     * @param targetPdfFilePath 合并后的pdf路径
     */
    @SneakyThrows
    public static File imagesToPdf(List<File> imageFiles, String targetPdfFilePath) {
        return imagesToPdf(imageFiles,null,null,1,FileUtil.file(targetPdfFilePath));
    }

    /**
     * 图片转PDF（每张图就是每页的PDF（每张根据图片的宽高进行调整））
     * 文档：https://www.x-easypdf.cn/#/md/pdfbox/%E5%8A%A9%E6%89%8B%E8%AF%B4%E6%98%8E?id=%e5%9b%be%e7%89%87%e6%9e%84%e5%bb%ba
     *
     * @param imageFiles        一系列图片文件
     * @param targetPdfFilePath 合并后的pdf路径
     */
    @SneakyThrows
    public static File imagesToPdf(List<File> imageFiles, File targetPdfFilePath) {
        return imagesToPdf(imageFiles,null,null,1,targetPdfFilePath);
    }


    /**
     * 图片转PDF（每张图就是每页的PDF（每张根据图片的宽高进行调整））
     * 文档：https://www.x-easypdf.cn/#/md/pdfbox/%E5%8A%A9%E6%89%8B%E8%AF%B4%E6%98%8E?id=%e5%9b%be%e7%89%87%e6%9e%84%e5%bb%ba
     *
     * @param imageFiles        一系列图片文件
     * @param imageFilter       文件过滤
     * @param sortComparator    【imageFiles】如何排序拼接成pdf == null则按默认排序
     * @param imageQuality      图片压缩
     * @param targetPdfFilePath 合并后的pdf路径
     */
    @SneakyThrows
    public static File imagesToPdf(List<File> imageFiles, FileFilter imageFilter, Comparator<File> sortComparator, Number imageQuality, File targetPdfFilePath) {
        imageFiles = CollUtil.emptyIfNull(imageFiles).stream()
                .filter(EnhanceImgUtil::isImage)
                .filter(imageFile -> imageFilter != null ? imageFilter.accept(imageFile) : true)
                .collect(Collectors.toList());

        // 排序
        if (sortComparator != null) {
            CollUtil.sort(imageFiles, sortComparator);
        }

        if (CollUtil.isNotEmpty(imageFiles) && targetPdfFilePath != null) {


            XEasyPdfDocument document = XEasyPdfHandler.Document.build();

            imageFiles.stream().forEachOrdered(imageFile -> {
                XEasyPdfComponent xEasyPdfImage = toEasyPdfImage(imageFile, ObjectUtil.defaultIfNull(imageQuality,1));
                XEasyPdfPage newPage = customNewPageByImage(imageFile);
                newPage.addComponent(xEasyPdfImage);
                document.addPage(newPage);
            });

            document.save(new FileOutputStream(targetPdfFilePath)).close();

            return targetPdfFilePath;
        }

        return null;
    }


    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param imageFilter       需要合并的图片
     * @param sortComparator    图片排序
     * @param imageQuality      图片压缩 0~1
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(File sourceImagesDir, FileFilter imageFilter, Comparator<File> sortComparator, Number imageQuality, File targetPdfFilePath) {
        if (!FileUtil.isDirectory(sourceImagesDir)) {
            log.warn("sourceImagesDir文件非目录：{}", FileUtil.getAbsolutePath(sourceImagesDir));
            return null;
        }

        // 仅需图片文件
        List<File> imageFiles = FileUtil.loopFiles(sourceImagesDir, EnhanceImgUtil::isImage);
        return imagesToPdf(imageFiles, imageFilter, sortComparator, imageQuality, targetPdfFilePath);
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param imageFilter       需要合并的图片
     * @param sortComparator    图片排序
     * @param imageQuality      图片压缩 0~1
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(String sourceImagesDir, FileFilter imageFilter, Comparator<File> sortComparator, Number imageQuality, String targetPdfFilePath) {
        return imagesToPdfByDir(FileUtil.file(sourceImagesDir), imageFilter, sortComparator, imageQuality, FileUtil.file(targetPdfFilePath));
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param imageFilter       需要合并的图片
     * @param sortComparator    图片排序
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(File sourceImagesDir, FileFilter imageFilter, Comparator<File> sortComparator, File targetPdfFilePath) {
        return imagesToPdfByDir(sourceImagesDir, imageFilter, sortComparator, null, targetPdfFilePath);
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param imageFilter       需要合并的图片
     * @param sortComparator    图片排序
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(String sourceImagesDir, FileFilter imageFilter, Comparator<File> sortComparator, String targetPdfFilePath) {
        return imagesToPdfByDir(sourceImagesDir, imageFilter, sortComparator, null, targetPdfFilePath);
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param sortComparator    图片排序
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(File sourceImagesDir, Comparator<File> sortComparator, File targetPdfFilePath) {
        return imagesToPdfByDir(sourceImagesDir, null, sortComparator, targetPdfFilePath);
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param sortComparator    图片排序
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(String sourceImagesDir, Comparator<File> sortComparator, String targetPdfFilePath) {
        return imagesToPdfByDir(sourceImagesDir, null, sortComparator, targetPdfFilePath);
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(File sourceImagesDir, File targetPdfFilePath) {
        return imagesToPdfByDir(sourceImagesDir, null, null, targetPdfFilePath);
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(String sourceImagesDir, String targetPdfFilePath) {
        return imagesToPdfByDir(sourceImagesDir, null, null, targetPdfFilePath);
    }


    /**
     * 图片转PDF
     *
     * @param sourceFileStr 单张图片文件或者存储图片文件的目录
     * @param targetFileStr 将上述sourceFileStr文件转成pdf后，pdf文件的路径
     */
    @SneakyThrows
    private static void imagesToPdfBySourFileType(String sourceFileStr, String targetFileStr) {
        if (StrUtil.isAllNotBlank(sourceFileStr, targetFileStr) && FileUtil.exist(sourceFileStr)) {
            if (FileUtil.isDirectory(sourceFileStr)) {
                imagesToPdfByDir(sourceFileStr, targetFileStr);
            } else {
                imagesToPdf(Arrays.asList(FileUtil.file(sourceFileStr)), targetFileStr);
            }
        }
    }

}
